//
// Copyright 2001-2004 Stottler Henke Associates, Inc.
// All Rights Reserved.
//////////////////////////////////////////////////////////////////////

package com.stottlerhenke.simbionic.engine.file;

import com.stottlerhenke.simbionic.api.SB_Exception;
import com.stottlerhenke.simbionic.common.SB_FileException;
import com.stottlerhenke.simbionic.common.SB_Logger;
import com.stottlerhenke.simbionic.common.SIM_Constants;
import com.stottlerhenke.simbionic.engine.SB_SingletonBook;
import com.stottlerhenke.simbionic.engine.core.EInternalActions;
import com.stottlerhenke.simbionic.engine.core.SB_Action;
import com.stottlerhenke.simbionic.engine.core.SB_BehaviorClass;
import com.stottlerhenke.simbionic.engine.core.SB_Function;
import com.stottlerhenke.simbionic.engine.core.SB_TypeHierarchy;
import com.stottlerhenke.simbionic.engine.core.SB_VariableMap;
import java.util.ArrayList;


/**
 * This class defines the format for project specification files
 * (ie sim files).
 */
public class SB_ProjectSpec extends SB_Specification
{
	/**
	 * Constructor
	 * @param reader the stream reader from which to read the specification
	 * @param projectName the name of the project
	 */
	public SB_ProjectSpec(SB_StreamReader reader,String projectName)
	{
		super(reader,projectName);
	}

	/**
	 * Reads the entire specification from the supplied stream.
	 * @param book container for all engine singletons
	 * @throws SB_FileException on failure
	 */
	public void read(SB_SingletonBook book) throws SB_FileException
	{
	  if (SIM_Constants.DEBUG_INFO_ON)
	  	book.getLogger().log("** Reading project spec '" + _specName + "'...");

		book.getFileRegistry().setProjectFilename(_specName);

		// verify a compatible sim-file format
		_reader.readSectionTag(SB_StreamReader.TAG_SIM_FORMAT);
		int formatVersion = _reader.readInt();
		if (!isCompatibleSpecFormat(formatVersion))
		{
			throw new SB_FileException("Incompatible sim file format: engine version "
										+ SIM_Constants.SIM_FORMAT_MAX_VERSION + ", file version " + formatVersion);
		}

		book.getFileRegistry().setFormatVersion(formatVersion);

	  if (SIM_Constants.DEBUG_INFO_ON)
	  	book.getLogger().log("** Specification format version " + formatVersion,SB_Logger.INIT);

		// read file version information
		_reader.readSectionTag(SB_StreamReader.TAG_FILE_VERSION);
		int specVersion = _reader.readInt();
		book.getFileRegistry().setSpecVersion(specVersion);

		readDescriptors(book);

		if( formatVersion >= 12) //First version where Classes are allowed
		{
		  readClasses(book);
		}
		
		if( (formatVersion < 9) || (formatVersion >= 12) ) //A few versions of the file do not have action and predicate descriptions built in
		{
		  readActions(book);
		  readPredicates(book);
		}

		try
		{
		  readGlobals(book);
		}
		catch(SB_Exception ex)
		{
		  throw new SB_FileException(ex.toString());
		}
		
		readBehaviors(book);

	  if (SIM_Constants.DEBUG_INFO_ON)
	  	book.getLogger().log("** Project spec file '" + _specName + "' loaded.",SB_Logger.INIT);
	}

	/**
	 * Loads descriptor hierarchies from specification.
	 * @param book container for all engine singletons
	 * @throws SB_FileException on failure
	 */
	private void readDescriptors(SB_SingletonBook book) throws SB_FileException
	{
		_reader.readSectionTag(SB_StreamReader.TAG_HIERARCHY_START);

		int numHiers = _reader.readInt();

	  if (SIM_Constants.DEBUG_INFO_ON)
	  	book.getLogger().log(".Loading " + numHiers + " hierarchies...",SB_Logger.INIT);

		for (int i=0; i<numHiers; i++)
		{
			SB_TypeHierarchy hier = new SB_TypeHierarchy();
			hier.load(_reader,book);

			book.getBehaviorRegistry().addHierarchy(hier);

		  if (SIM_Constants.DEBUG_INFO_ON)
		  	book.getLogger().log(hier.toString(),SB_Logger.INIT);
		}
	}

	/**
	 *  Read the clas types from the file
	 * 
	 * @param book
	 * @throws SB_FileException
	 */
	private void readClasses(SB_SingletonBook book) throws SB_FileException
	{
		_reader.readSectionTag(SB_StreamReader.TAG_CLASSES_START);

		int numClasses = _reader.readInt();
		
		for( int i = 0; i < numClasses; i++ )
		{
			Integer classId = new Integer(_reader.readInt());
			String classPackage = _reader.readPString();
			String className = _reader.readPString();
	    
			book.getUserClassMap().addSBClass(classId, className);
			book.getUserClassMap().addJavaClass(className, classPackage);
			
			if (SIM_Constants.DEBUG_INFO_ON)
		  	book.getLogger().log("Loaded class: " + classPackage, SB_Logger.INIT);
			
			if( book.getFileRegistry().getFormatVersion() >= 13)
			{
			  readMethods(book, classPackage);
			  readMembers(book, classPackage);
			}
		}
		
		//Now that all classes read in, convert class descriptions
		try
		{
		  book.getUserClassMap().convertClassDescriptions(book);
		}
		catch(SB_Exception ex)
		{
		  throw new SB_FileException(ex.toString());
		}
	}
	
	private void readMethods(SB_SingletonBook book, String classPackage)
		throws SB_FileException
	{
	  int numMethods = _reader.readInt();
	  for( int x = 0; x < numMethods; x++ )
	  {
	    int varType = _reader.readInt();
	    String methodName = _reader.readPString();
	    ArrayList params = readParams(book);
	    
	    book.getUserClassMap().getClassDescription(classPackage).addMethodDescription(methodName, varType, params);
	    
			if (SIM_Constants.DEBUG_INFO_ON)
		  	book.getLogger().log("  Loaded class method: " + methodName + " return type: " + varType, SB_Logger.INIT);

	  }
	}
	
	private ArrayList readParams(SB_SingletonBook book)
		throws SB_FileException
	{
	  ArrayList params = new ArrayList();
	  
	  int numParams = _reader.readInt();
	  for( int x = 0; x < numParams; x++ )
	  {
	    int varType = _reader.readInt();
	    params.add(new Integer(varType));
	  } 
	  
	  return params;
	}
	
	private void readMembers(SB_SingletonBook book, String classPackage)
		throws SB_FileException
	{
	  int numMembers = _reader.readInt();
	  for( int x = 0; x < numMembers; x++ )
	  {
	    int varType = _reader.readInt();
	    String memberName = _reader.readPString();
	    
	    book.getUserClassMap().getClassDescription(classPackage).addMemberDescription( memberName, varType);

			if (SIM_Constants.DEBUG_INFO_ON)
		  	book.getLogger().log("  Loaded class member: " + memberName + " " + varType, SB_Logger.INIT);

	  }
	}
	
	/**
	 * Loads actions from specification. Only happens for file version < 9
         *
	 * @param book container for all engine singletons
	 * @throws SB_FileException on failure
	 */
	private void readActions(SB_SingletonBook book) throws SB_FileException
	{
		_reader.readSectionTag(SB_StreamReader.TAG_ACTIONS_START);

		int numActions = _reader.readInt();

    if(SIM_Constants.DEBUG_INFO_ON)
      book.getLogger().log(".Loading " + numActions + " Actions...",SB_Logger.INIT);

    for (int i = 0; i < numActions; i++) 
    {
      SB_Action action = new SB_Action();
      action.load(_reader,book);
      //Add the interface for version 9 and up compatability (A/P Modules)
      if( SB_Action.IsReserved(action.GetId()) )
      {
        action.SetInterface(book.getCoreInterface());
      }
      else
        action.SetInterface(book.getSimInterface().GetSim());
      book.getBehaviorRegistry().addAction(action);

      if(SIM_Constants.DEBUG_INFO_ON)
      	book.getLogger().log(action.toString());
    }

    if(SIM_Constants.DEBUG_INFO_ON)
      book.getLogger().log(".Loaded " + numActions + " Actions.",SB_Logger.INIT);

    SB_Action none = new SB_Action("None",EInternalActions.ID_ACT_NONE.getState());
    none.SetInterface(book.getCoreInterface());
    book.getBehaviorRegistry().addAction(none);
	}

	/**
	 * Loads predicates from specification.
	 * @param book container for all engine singletons
	 * @throws SB_FileException on failure
	 */
	private void readPredicates(SB_SingletonBook book) throws SB_FileException
	{
		_reader.readSectionTag(SB_StreamReader.TAG_PREDICATES_START);

		int numPredicates = _reader.readInt();

    if(SIM_Constants.DEBUG_INFO_ON)
      book.getLogger().log(".Loading " + numPredicates + " predicates...",SB_Logger.INIT);

    for (int i = 0; i < numPredicates; i++) {
      SB_Function fun = new SB_Function();
      fun.load(_reader,book);
      //Add the interface for version 9 and up compatability (A/P Modules)
      if( SB_Function.IsReserved(fun.GetId()) )
        fun.SetInterface(book.getCoreInterface());
      else
        fun.SetInterface(book.getSimInterface().GetSim());
      book.getBehaviorRegistry().addPredicate(fun);

      if(SIM_Constants.DEBUG_INFO_ON)
      	book.getLogger().log(fun.toString());
    }
    if(SIM_Constants.DEBUG_INFO_ON)
    	book.getLogger().log(".Loaded " + numPredicates + " predicates.",SB_Logger.INIT);
	}

	/**
	 * Loads global variables from specification.
	 * @param book container for all engine singletons
	 * @throws SB_FileException on failure
	 */
	private void readGlobals(SB_SingletonBook book) throws SB_FileException, SB_Exception
	{
		_reader.readSectionTag(SB_StreamReader.TAG_GLOBALS_START);
		SB_VariableMap globals = new SB_VariableMap();
		globals.load(_reader,book,true);
		book.getEntityManager().setGlobalTemplate(globals);
	}

	/**
	 * Loads behaviors from specification.
	 * @param book container for all engine singletons
	 * @throws SB_FileException on failure
	 */
	private void readBehaviors(SB_SingletonBook book) throws SB_FileException
	{
		_reader.readSectionTag(SB_StreamReader.TAG_BEHAVIORS_START);

		int numBehaviors = _reader.readInt();

		// read the behaviors one-by-one from the file
		SB_BehaviorClass behavior;
		for (int i=0; i < numBehaviors; i++)
		{
			// set the behavior's unique ID on creation
			behavior = new SB_BehaviorClass( book.getBehaviorRegistry().requestBehaviorId() );

			behavior.load(_reader, book);

			book.getBehaviorRegistry().addBehavior(behavior);
			book.getFileRegistry().associatePackage(_specName,behavior.getName());
		}
	}


}
